Wheels: link FreeType against the bundled HarfBuzz - #9913
Conversation
FreeType's autofitter can use HarfBuzz to work out which glyphs a script covers, so it can derive blue zones for scripts such as Arabic. Only the Windows wheels were built to allow that. macOS passed --with-harfbuzz=no, and Linux left it unconfigured, which meant FreeType defaulted to `dlopen`ing HarfBuzz at runtime with the name "libharfbuzz.so.0". `auditwheel`, part of the wheeling process, renames shared libraries to avoid conflicts, so the Linux wheels ended up with a FreeType that would only find HarfBuzz if the system had its own copy with the name "libharfbuzz.so.0"; the bundled copy was never used.
This comment was marked as outdated.
This comment was marked as outdated.
| fi | ||
| # FreeType and HarfBuzz each want the other: | ||
| # HarfBuzz reads font data through FreeType, and FreeType's autofitter asks HarfBuzz which glyphs a script covers. | ||
| # Break the cycle by building FreeType twice, so that the FreeType we ship is linked against the HarfBuzz we ship. |
There was a problem hiding this comment.
It is unusual that this should be required, right? Have FreeType or Harfbuzz discussed this anywhere?
There was a problem hiding this comment.
Google search autocomplete (on a different device, on mobile right now) suggests https://www.google.com/search?q=freetype+harfbuzz+circular+dependency if you start typing "freetype harf", so that suggests it's a known thing.
Here's a recent-ish Debian bug about it https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1124239
Freetype notes the circular dependency in the release announcement for 2.14.0 on https://freetype.org/
There was a problem hiding this comment.
The conclusion of the Debian bug and the FreeType release notes that the solution is to load HarfBuzz dynamically. Are you going to tell me that option isn't available to us because of auditwheel?
There was a problem hiding this comment.
As noted in the PR description, FT does attempt to load HarfBuzz dynamically on main already, but yes - auditwheel renames the bundled dynamic library and fixes up import tables but can't fix the dynamic dlopen call within FT.
The dlopen name is specified like https://github.com/freetype/freetype/blob/master/src/autofit/ft-hb.c#L34 so we'd need to know the name auditwheel will rename harfbuzz to before we compile FT to pass that preprocessor constant.
|
@khaledhosny because you're obviously thinking about this sort of thing at the moment, did you have any thoughts on this approach? |
This is the usual solution to this issue; build freetype without harfbuzz support, then build harfbuzz with freetype support, then build freetype again with harfbuzz support. The new way is to let freetype load harfbuzz at runtime which is discussed above. You can break this circular dependency by either not using FreeType’s font functions in HarfBuzz and use HarfBuzz’s internal font functions, but if you are using horizontal hinting in FreeType you might get different glyph advance widths. Or by not using HarfBuzz in FreeType but the auto hinter will give different results which I think this is what this issue is trying to fix. |
Follows up on #8497.
This came up as a side effect of #9909, where the last commit needed to increase the allowed comparison epsilon for the Arabic-language test, since different platforms in the CI stack would render it slightly differently... and it's a doozy! 😄
FreeType's autofitter can use HarfBuzz to work out which glyphs a script covers, so it can derive blue zones for scripts such as Arabic.
Only the Windows wheels were built that way. macOS passed
--with-harfbuzz=no(so no HarfBuzz interop, no way, no how), and Linux left it unconfigured, which meant FreeType defaulted todlopening HarfBuzz at runtime with the name "libharfbuzz.so.0".However,
auditwheelrenames shared libraries to avoid conflicts, so the Linux wheels ended up with a FreeType that would only find HarfBuzz if the system had its own copy with the name "libharfbuzz.so.0".In the current 12.3 wheel off PyPI, the interop symbols do not exist:
but in a wheel built by CI off this branch (see akx#18):